#incldue "Connection.h"
#include "NetworkSystem.h"
#include "Config.h"

#ifdef WIN32
WSADATA _wsaHandle;
#endif

int WINAPI ConnectionHandler(void* data)
{
	Connection* connection = (Connection*)data;
	NetworkMessage msg;
	while (connection.IsOpen())
	{
		if (connection.Read(&msg))
		{
			if (msg.GetLength() > 0)
			{
				netSys.DecodeMessage(connection, &msg); // fire events if needed
			}
		}
	}
	netSys.Close(connection);
	connection = NULL;
	return 0;
}

NetworkSystem::NetworkSystem()
{
	RunStatus = 0; // > 0: running; < 1: stopping; = 0: Not running
	Host = "0.0.0.0"; // all addresses
	Port = "5096";
#ifdef WIN32
	int res = WSAStartup(MAKEWORD(2,2), &_wsaHandle); // use winsock 2.2
	if (res != 0)
	{
		if (res == WSASYSNOTREADY)
		{
			fprintf(stderr, "ModuleControl: Network subsystem is not ready at this time. Please try again later.\n");
		}
		else if (res == WSAVERNOTSUPPORTED)
		{
			fprintf(stderr, "ModuleControl: Cannot startup WinSock system. Please ensure that Winsock version 2.2 is installed on the system.\n");
		}
		else if (res == WSAEPROCLIM)
		{
			fprintf(stderr, "ModuleControl: Too many running processes to start the WinSock system. Please free up some system resources and try again.\n");
		}
		return;
	}
#endif
}

NetworkSystem::~NetworkSystem()
{
	Stop();
	while (RunStatus != 0) // wait as the system flushes all messages in the queue
	{
		PauseThread(500);
	}
#ifdef WIN32
	WSACleanup();
#endif
}

void NetworkSystem::Init()
{
	std::string host, port;
	host = conf.GetValue("network.listen.host");
	port = conf.GetValue("network.listen.port");
	if (host != "")
	{
		Host = host;
	}
	if (Port != "")
	{
		Port = port;
	}
}

void NetworkSystem::Run()
{
	sock_t handle;
	sock_t client;
	Connection* conn;
	int res;
	struct addrinfo info, *info2;
	memset(info, 0, sizeof(addrinfo));
	info.ai_family = AF_INET;
	info.ai_socktype = SOCK_STREAM;
	info.ai_protocol = IPPROTO_TCP;
	info.ai_flags = AI_PASSIVE;
	res = getaddrinfo(ListenHost.c_str(), ListenPort.c_str(), &info, &info2);
	if (!res)
	{
		fprintf(stderr, "NetworkSystem.Run: Could not get address information. %d\n", ErrorCode());
		return;
	}
	handle = socket(info2->ai_familty, info2->ai_socktype, info2->ai_protocol);
	if (!handle)
	{
		fprintf(stderr, "NetworkSystem.Run: Could not create socket. %s", ErrorCode());
		return;
	}
	res = bind(handle, info2->ai_addr, (int)info2->ai_addrlen);
	if (res != 0)
	{
		fprintf(stderr, "NetworkSystem.Run: Could not bind socket. %d\n", ErrorCode());
		close(handle);
		return;
	}
	freeaddrinfo(info2);
	while ((res = listen(handle, SOMAXCONN)) != 0 && RunStatus > 0)
	{
		client = accept(handle, NULL, NULL);
		if (!client)
		{
			fprintf(stderr, "ModuleControl.Run: Could not accept connection. %d\n", GetErrorCode());
			break;
		}
		conn = new Connection(client);
		NewThread(ConnectionHandler, conn);
		Connections.push_back(conn);
	}
	fprintf(stderr, "NetworkSystem.Run: Stopping queue processing threads\n");
	CloseAllConnections();
	RunStatus = 0;
}

void NetworkSystem::Close(Connection* conn)
{
	std::list<Connection*>::iterator iter = Connections.find(conn);
	if (iter != Connections.end())
	{
		iter->Close();
		Connections.erase(iter);
	}
}

void NetworkSystem::CloseAllConnections()
{
	std::list<Connection*>::iterator iter = Connections.begin();
	while (iter != Connections.end())
	{
		iter->Close();
		Connections.erase(iter);
		iter = Connections.begin();
	}
}

void NetworkSystem::DecodeMessage(Connection* conn, NetworkMessage* msg)
{
	NetworkMessage msg1, msg2;
	msg1 = crypto.Decrypt(*msg);
	unsigned long e = msg1.ReadDoubleWord();
	msg2.Copy(msg1);
	std::list<NetworkMessage> resp;
	EventMutex.Lock();
	std::map<unsigned long, std::list<IBackEndModule*> >::iterator iter = Events.find(e);
	if (iter != Events.end())
	{
		std::list<IBackEndModule*>::iterator it = iter->second.begin();
		for (; it != iter->second.end(); it++)
		{
			msg1 = it->OnEvent(e, msg2);
			resp.push_back(msg1);
		}
	}
	EventMutex.Unlock();
	std::list<NetworkMessage>::iterator it2 = resp.begin();
	for (; it2 != resp.end(); it2)
	{
		msg1 = crypto.Encrypt(*it2);
		conn->Write(&msg1);
	}
}
